package pl.edu.agh.logview;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerDateModel;
import javax.swing.SpinnerModel;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import pl.edu.agh.logview.graph.GraphManager2;
import pl.edu.agh.logview.model.Edge;
import pl.edu.agh.logview.model.Vertex;
import pl.edu.agh.logview.model.VertexType;
import pl.edu.agh.logview.webservice.Server;

import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
import com.toedter.calendar.JDateChooser;

public class App extends JFrame {

	private static final long serialVersionUID = 1L;

	private GraphManager2 graphManager;
	private mxGraph graph;
	private mxGraphComponent graphComponent;
	private Object parent;
	private List<Object> endingVertices = new LinkedList<Object>();
	private List<Object> agentVertices = new LinkedList<Object>();
	private JPanel regexpMenu = null;
	private JButton acceptSettingsButton = null;
	private JTextField regexpInput = null;
	private JFrame mainFrame = null;

	public App() {
		
		mainFrame = this;
		
		graphManager = new GraphManager2();
		graph = new mxGraph();
		parent = graph.getDefaultParent();
		graph.setEnabled(false);
		graph.setCellsLocked(true);
		graph.setCellsSelectable(false);
		prepareGraph();
		graphComponent = new mxGraphComponent(graph);
		
		setLayout(new BorderLayout());
		regexpMenu = new JPanel();
		regexpMenu.setLayout(new FlowLayout());
		
		acceptSettingsButton = new JButton("Zatwierdz");
		regexpInput = new JTextField();
		regexpInput.setColumns(20);
		regexpInput.setText("Wyrażenie regularne");
		regexpInput.setToolTipText("Wprowadź wyrażenie regularne opisujące nazwę Agenta");
		
		final JDateChooser startDateChooser = new JDateChooser();
		startDateChooser.setDate(Calendar.getInstance().getTime());
		
		for (Component comp : startDateChooser.getComponents()) {
		    if (comp instanceof JTextField) {
			((JTextField) comp).setEditable(false);
		    }
		}
		
		final JDateChooser endDateChooser = new JDateChooser();
		endDateChooser.setDate(Calendar.getInstance().getTime());
		
		for (Component comp : endDateChooser.getComponents()) {
		    if (comp instanceof JTextField) {
			((JTextField) comp).setEditable(false);
		    }
		}
		
		SpinnerModel startModel = new SpinnerDateModel();
		final JSpinner startTimeSpinner = new JSpinner(startModel);
		JComponent startEditor = new JSpinner.DateEditor(startTimeSpinner, "HH:mm:ss");
		startTimeSpinner.setEditor(startEditor);
		startTimeSpinner.setValue(Calendar.getInstance().getTime());
		
		SpinnerModel endModel = new SpinnerDateModel();
		final JSpinner endTimeSpinner = new JSpinner(endModel);
		JComponent endEditor = new JSpinner.DateEditor(endTimeSpinner, "HH:mm:ss");
		endTimeSpinner.setEditor(endEditor);
		endTimeSpinner.setValue(Calendar.getInstance().getTime());

		final JCheckBox checkBox = new JCheckBox();
		checkBox.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) {
					graphManager.setFixedSpace(checkBox.isSelected());
					prepareGraph();				
			}
		});
		
		regexpMenu.add(new JLabel("Wiadomości od:"));
		regexpMenu.add(startTimeSpinner);
		regexpMenu.add(startDateChooser);
		regexpMenu.add(new JLabel("Wiadomości do:"));
		regexpMenu.add(endTimeSpinner);
		regexpMenu.add(endDateChooser);
		regexpMenu.add(regexpInput);
		regexpMenu.add(acceptSettingsButton);
		regexpMenu.add(new JLabel("Czy równy odstęp?"));
		regexpMenu.add(checkBox);
		regexpMenu.setPreferredSize(new Dimension(200, 60));
		
		acceptSettingsButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Date startTime = (Date) startTimeSpinner.getModel().getValue();
				Date startDate = startDateChooser.getDate();
				Calendar startCal = Calendar.getInstance();
				startCal.set(startDate.getYear()+1900, startDate.getMonth(), startDate.getDate(), startTime.getHours(), startTime.getMinutes(), startTime.getSeconds());
				
				Date endTime = (Date) endTimeSpinner.getModel().getValue();
				Date endDate = endDateChooser.getDate();
				Calendar endCal = Calendar.getInstance();
				endCal.set(endDate.getYear()+1900, endDate.getMonth(), endDate.getDate(), endTime.getHours(), endTime.getMinutes(), endTime.getSeconds());
				
				graphManager.setFromDate(startCal.getTime());
				graphManager.setToDate(endCal.getTime());
				graphManager.setRegexp(regexpInput.getText());
				prepareGraph();
			}
		});
		getContentPane().add(graphComponent,BorderLayout.CENTER);
		getContentPane().add(regexpMenu,BorderLayout.PAGE_START);
		
		graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
			@Override       
			public void mousePressed(MouseEvent e) {            
				Object cell = graphComponent.getCellAt(e.getX(), e.getY());            
				System.out.println("Mouse click in graph component");          
				if (cell != null) {                
					if (cell instanceof mxCell) {
						Object val = ((mxCell) cell).getValue();
						if (val instanceof Vertex) {
							Vertex v = (Vertex) val;
							if (v.getVertexType() == VertexType.VERTEX_STATE) {
								JOptionPane.showMessageDialog(App.this, v.getName());

							}
						} if (val instanceof Edge) {
							Edge edge = (Edge)val;
							if (edge.isClickable()) {
								CustomDialog myDialog = new CustomDialog(mainFrame, true, edge.getOnClickMessage());
								myDialog.setSize(new Dimension(200, 200));
							}
						}
					} 
				}
			}
		});
		
	}

	// usuwa wszystko i rysuje od nowa
	private void prepareGraph() {
		graph.getModel().beginUpdate();
		try {
			if (graphManager == null)
				return;
			graph.removeCells(graph.getChildVertices(graph.getDefaultParent()));
			graphManager.refresh();

			for (Vertex vertex : graphManager.getAgentVertices()) {
				Object o = graph.insertVertex(parent, null, vertex, vertex
						.getX(), vertex.getY(), vertex.getVertexType()
						.getWidth(), vertex.getVertexType().getHeight(), vertex
						.getVertexType().getStyle());
				vertex.setVertexObject(o);
				agentVertices.add(o);
			}
			for (Vertex vertex : graphManager.getMessageVertices()) {
				vertex.setVertexObject(graph.insertVertex(parent, null, vertex,
						vertex.getX(), vertex.getY(), vertex.getVertexType()
								.getWidth(),
						vertex.getVertexType().getHeight(), vertex
								.getVertexType().getStyle()));
			}
			for (Vertex vertex : graphManager.getStateVertices()) {
				vertex.setVertexObject(graph.insertVertex(parent, null, vertex,
						vertex.getX(), vertex.getY(), vertex.getVertexType()
								.getWidth(),
						vertex.getVertexType().getHeight(), vertex
								.getVertexType().getStyle()));
			}
			endingVertices.clear();
			for (Vertex vertex : graphManager.getEndingVertices()) {
				Object o = graph.insertVertex(parent, null, vertex, vertex
						.getX(), vertex.getY(), vertex.getVertexType()
						.getWidth(), vertex.getVertexType().getHeight(), vertex
						.getVertexType().getStyle());
				vertex.setVertexObject(o);
				endingVertices.add(o);
			}

			for (Edge e : graphManager.getMessageEdges()) {
				e.setGraphEdge(graph.insertEdge(parent, null, e, e.from()
						.getVertexObject(), e.to().getVertexObject(), e
						.getEdgeType().getStyle()));
			}
			for (Edge e : graphManager.getAgentEdges()) {
				e.setGraphEdge(graph.insertEdge(parent, null, e, e.from()
						.getVertexObject(), e.to().getVertexObject(), e
						.getEdgeType().getStyle()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			graph.getModel().endUpdate();

		}
	}

	// aktualizuje graf
	private void updateGraph() {
		graph.getModel().beginUpdate();
		try {
			if (graphManager == null)
				return;
			graphManager.refresh();
			graph.removeCells(agentVertices.toArray(new Object[0]));
			agentVertices.clear();
			for (Vertex vertex : graphManager.getAgentVertices()) {
				Object o = graph.insertVertex(parent, null, vertex, vertex
						.getX(), vertex.getY(), vertex.getVertexType()
						.getWidth(), vertex.getVertexType().getHeight(), vertex
						.getVertexType().getStyle());
				vertex.setVertexObject(o);
				agentVertices.add(o);
			}
			for (Vertex vertex : graphManager.getNewMessageVertices()) {
				vertex.setVertexObject(graph.insertVertex(parent, null, vertex,
						vertex.getX(), vertex.getY(), vertex.getVertexType()
								.getWidth(),
						vertex.getVertexType().getHeight(), vertex
								.getVertexType().getStyle()));
			}
			for (Vertex vertex : graphManager.getNewStateVertices()) {
				vertex.setVertexObject(graph.insertVertex(parent, null, vertex,
						vertex.getX(), vertex.getY(), vertex.getVertexType()
								.getWidth(),
						vertex.getVertexType().getHeight(), vertex
								.getVertexType().getStyle()));
			}
			graph.removeCells(endingVertices.toArray(new Object[0]));
			endingVertices.clear();
			for (Vertex vertex : graphManager.getEndingVertices()) {
				Object o = graph.insertVertex(parent, null, vertex, vertex
						.getX(), vertex.getY(), vertex.getVertexType()
						.getWidth(), vertex.getVertexType().getHeight(), vertex
						.getVertexType().getStyle());
				vertex.setVertexObject(o);
				endingVertices.add(o);
			}
			for (Edge e : graphManager.getNewMessageEdges()) {
				e.setGraphEdge(graph.insertEdge(parent, null, e, e.from()
						.getVertexObject(), e.to().getVertexObject(), e
						.getEdgeType().getStyle()));
			}
			for (Edge e : graphManager.getAgentEdges()) {
				e.setGraphEdge(graph.insertEdge(parent, null, e, e.from()
						.getVertexObject(), e.to().getVertexObject(), e
						.getEdgeType().getStyle()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			graph.getModel().endUpdate();
		}
	}

	private void refresh() {
		while (true) {
			MySwingWorker worker = new MySwingWorker();
			worker.execute();
			while (!worker.isDone()) {
			}
		}
	}

	public static void main(String[] argv) {
		Server server = new Server();
		App frame = new App();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(800, 820);
		frame.setVisible(true);
		frame.refresh();
	}

	private class MySwingWorker extends SwingWorker<Void, Void> {
		@Override
		protected Void doInBackground() throws Exception {
			System.out.println("doInBg");
			Thread.sleep(5000);
			updateGraph();
			return null;
		}

		@Override
		public void done() {
			// getContentPane().repaint();
		}

	}
}
